package com.xiaokan.query;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import com.xiaokan.datastructures.Heap;
import com.xiaokan.datastructures.LexiconDictionary;
import com.xiaokan.datastructures.URLDictionary;
import com.xiaokan.pojo.Lexicon;
import com.xiaokan.pojo.URL;
import com.xiaokan.pojo.URLWrapper;
import com.xiaokan.utils.DataManager;
import com.xiaokan.utils.MyProperties;

public class QueryProcessor {

	private static QueryProcessor instance = new QueryProcessor();
	private URLDictionary uDict = DataManager.getInstance().readURLDict();
	private LexiconDictionary lDict = LexiconDictionary.getInstance();
	private double k1 = 1.2, b = 0.75;

	private QueryProcessor() {

	}

	public static QueryProcessor getInstance() {
		return instance;
	}

	public URLWrapper[] query(String query, String seperator, int maxDocs) {
		URLWrapper[] ret = new URLWrapper[maxDocs];
		query = query.trim();
		if (query.length() == 0)
			return new URLWrapper[0];
		String[] terms = query.split(seperator);
		List<URLWrapper> list = new ArrayList<URLWrapper>();
		int did = 0, num = terms.length, sumOfDocLength = 0;
		IndexWrapper wrs[] = new IndexWrapper[num];
		for (int i = 0; i < num; i++) {
			wrs[i] = new IndexWrapper();
			if (!wrs[i].open(terms[i]))
				return null;
		}

		Lexicon lexes[] = new Lexicon[num];
		for (int i = 0; i < num; i++) {
			lexes[i] = lDict.getLexicon(terms[i]);
			if (lexes[i] == null)
				return null;
		}

		while (did < IndexWrapper.MAXDID) {
			/* get next post from shortest list */
			did = wrs[0].nextGEQ(did);
			int d = 0;
			if (did >= IndexWrapper.MAXDID)
				break;
			/* see if find entries with same docId in other lists */
			for (int i = 1; (i < num) && ((d = wrs[i].nextGEQ(did)) == did); i++) {
			}
			if (d > did)
				did = d; // not in intersection
			else {
				/* docId is in intersection; get all freqs */
				URL u = uDict.getURLByID(did);
				// if (u == null)
				// System.out.println(did);
				u.setId(did);
				if (u.getStartFrom() < 0) {
					System.out.println(u.getUrl());
					System.out.println(u.getStartFrom());
					System.out.println(u.getEndwith());
				}
				sumOfDocLength += u.getLength();
				URLWrapper w = new URLWrapper();
				w.setUrl(u);
				int freqs[] = new int[num];
				for (int i = 0; i < num; i++)
					freqs[i] = wrs[i].getFreq();
				w.setFreqs(freqs);
				list.add(w);
				u = null;
				w = null;
				freqs = null;
				did++;
			}
		}

		/* Calculating bm25 score */
		Heap<URLWrapper> heap = new Heap<URLWrapper>(maxDocs);
		int N = uDict.size();
		double avgLength = (double) sumOfDocLength / (double) N;
		double cons1 = k1 * (1 - b);// k1*(1-b)
		for (URLWrapper uw : list) {
			double bm25 = 0.0;
			double K = cons1 + k1 * b * uw.getUrl().getLength() / avgLength;
			for (int i = 0; i < num; i++) {
				int f_t = lexes[i].getNumerOfDocs();
				int f_d_t = uw.getFreqs()[i];
				bm25 += this.log2((N - f_t + 0.5) / (f_t + 0.5)) * (k1 + 1) * f_d_t / (K + f_d_t);
			}
			uw.setBm25Value(bm25);
			if (heap.getSize() < maxDocs) {
				heap.add(uw);
			} else if (heap.getSize() >= maxDocs && bm25 > heap.getTop().getBm25Value()) {
				heap.pop();
				heap.add(uw);
			}
		}

		/* Set Return results */
		int i = 0;
		while (heap.getTop() != null) {
			ret[i++] = heap.pop();
		}

		/* Disposing Index Wrappers */
		for (i = 0; i < num; i++) {
			wrs[i].close();
			wrs[i] = null;
		}
		/* Disposing vars */
		for (i = 0; i < terms.length; i++)
			terms[i] = null;
		for (i = 0; i < lexes.length; i++)
			lexes[i] = null;
		lexes = null;
		list.clear();
		heap.clear();
		list = null;
		terms = null;
		heap = null;
		wrs = null;
		return ret;
	}

	public URLWrapper[] query(String query, int maxDocs) {
		return this.query(query, MyProperties.PATTERN_SPLIT, maxDocs);
	}

	private double log2(double num) {
		return Math.log(num) / Math.log(2);
	}

}
